package cn.xd.security.filter;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.xd.common.constant.AuthConstant;
import cn.xd.common.exception.BizException;
import cn.xd.security.config.SecurityProperties;
import cn.xd.security.domain.cache.CustomerUserCacheUserInfo;
import com.alibaba.fastjson2.JSON;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Component
@Slf4j
public class AuthenticationFilter extends OncePerRequestFilter {
    private static final String TOKEN_ILLEGAL = "40001";
    private static final String TOKEN_KICK = "40002";
    private static final String TOKEN_INVALID = "40003";
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private SecurityProperties securityProperties;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 获取当前请求的uri

        String uri = request.getRequestURI();
        log.info("请求路径:" + uri);
        PathMatcher pathMatcher = new AntPathMatcher();
        // 判断是否是认证请求路径
        // 是：直接放行
        boolean publicUrl = Boolean.FALSE;
        for (String item : securityProperties.getPublicUrl()) {
            publicUrl = publicUrl || pathMatcher.match(item, uri);
        }

        if (publicUrl) {
            filterChain.doFilter(request, response);
            return;
        }
        // 否：获取请求头中携带的token
        String authorization = request.getHeader("Authorization");
        log.info("携带authorization:" + authorization);
        // 判断是否携带token
        // 否：抛出异常
        if (StrUtil.isBlank(authorization)) {
            throw new BizException("无效token");
        }
        String token = authorization.replace("Bearer ", "");
        if (StrUtil.isBlank(token)) {
            throw new BizException("无效token");
        }

        String userInfoKey = String.format(AuthConstant.AUTH_USER_INFO_FORMAT, token);

        String permKey = String.format(AuthConstant.AUTH_USER_PERM_FORMAT, token);
        Object cacheObject = redisTemplate.opsForValue().get(userInfoKey);
        Set<Object> permKeyObject = redisTemplate.opsForSet().members(permKey);
        Set<SimpleGrantedAuthority> simpleGrantedAuthorities = new HashSet<>();
        if (ObjUtil.isEmpty(cacheObject)) {
            throw new BizException("token已过期,请重新登录");
        }
        //消费者全部权限
        if (cacheObject instanceof CustomerUserCacheUserInfo) {
            simpleGrantedAuthorities.add(new SimpleGrantedAuthority("/**"));
        } else {
            if (CollUtil.isNotEmpty(permKeyObject)) {
                simpleGrantedAuthorities = permKeyObject.stream().map(Objects::toString)
                        .map(SimpleGrantedAuthority::new).collect(Collectors.toSet());
            }
        }
        // 放行
        request.setAttribute(AuthConstant.USER_TOKEN_HEADER, JSON.toJSONString(cacheObject));
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                new UsernamePasswordAuthenticationToken(cacheObject, null, simpleGrantedAuthorities);
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        filterChain.doFilter(request, response);
    }
}
